/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 * Copyright @2008 the original author or authors.
 */

package com.bluestone.util;

import java.awt.GraphicsEnvironment;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Stack;
import java.util.StringTokenizer;

import javax.imageio.ImageIO;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.bluestone.assertor.IAssertor;
import com.bluestone.config.BluestoneConfig;
import com.bluestone.context.ProjectContext;
import com.bluestone.scripts.Element;
import com.bluestone.scripts.Input;
import com.bluestone.scripts.Module;
import com.bluestone.scripts.Page;
import com.bluestone.scripts.ParserManager;
import com.bluestone.scripts.Project;
import com.bluestone.scripts.Property;
import com.bluestone.scripts.Resource;
import com.bluestone.scripts.TestCase;
import com.bluestone.scripts.TestSuite;

/**
 * @author <a href="mailto:bluestone.daniel.q@gmail.com">daniel.q</a>
 */
public class Util {
	static Robot robot = null;

	static Logger logger = null;

	static Page forwardpage = null;

	static Stack stackPages = new Stack();

	static LinkedList linkActions = new LinkedList();

	public static int SPEEDRATE = 100;

	public static int DELAY20 = 20;

	public static int DELAY50 = 50;

	public static int DELAY100 = 100;

	public static int DELAY200 = 200;

	public static int DELAY300 = 300;

	public static int DELAY400 = 400;

	public static int DELAY500 = 500;

	public static int DELAY600 = 600;

	public static int DELAY700 = 700;

	public static int DELAY800 = 800;

	public static int DELAY900 = 900;

	public static int DELAY1000 = 1000;

	public static int DELAY2000 = 2000;

	public static int DELAY3000 = 3000;

	public static int INPUTTEXTDELAY = 300;

	public static int MOVEDELAY = 500;

	public static int CLICKDELAY = 500;

	public static int CHECKDELAY = 2000;

	public static int CLOSEPAGEDELAY = 1000;

	public static int INPUTKEYDELAY = 500;

	public static int MAXPAGEDELAY = 1500;

	public static int CLICKURLDELAY = 3000;

	public static int FOCUSDELAY = 300;

	public static int RUNDELAY = 300;
	public static int SAVEASDELAY = 500;
	public static int COMPAREIMAGEDELAY = 300;

	public static int MINPAGEDELAY = 1000;

	public static int CALLFUNCTIONDELAY = 500;

	public static String ERROR1 = "Can't find object ";

	private static long actionId = 0;
	private static long projectId = 0;
	private static long testSuitId = 0;
	private static long testCaseId = 0;
	private static long commandId = 0;
	private static long reportBeanId = 0;
	public static String ACTION_ID = "ACTIONID";
	public static String PROJECT_ID = "PROJECTID";
	public static String TESTSUIT_ID = "TESTSUITID";
	public static String TESTCASE_ID = "TESTCASEID";
	public static String COMMAND_ID = "COMMANDID";
	public static String REPORTBEAN_ID = "REPORTBEANID";

	private static HashMap dynamicParaMap = new HashMap();

	private static HashMap dynamicPropMap = new HashMap();

	private static HashMap dynamicVarMap = new HashMap();

	private static String currentDate = "";

	private static String currentTime = "";

	public static String getCurrentDate() {
		return currentDate;
	}

	public static void setCurrentDate(String currentDate) {
		Util.currentDate = currentDate;
	}

	public static String getCurrentTime() {
		return currentTime;
	}

	public static void setCurrentTime(String currentTime) {
		Util.currentTime = currentTime;
	}

	public static int getDelayTime(int ms) {
		Project project = ProjectContext.getInstance(null).getProject();
		if (project != null) {
			return Integer.parseInt(project.getSpeedrate()) * ms / 100;
		} else {
			return SPEEDRATE * ms / 100;
		}
	}

	public static void error(Object obj) {
		getLogger().error(getLogObject(obj));
	}

	public static void info(Object obj) {
		getLogger().info(getLogObject(obj));
	}

	public static void warn(Object obj) {
		getLogger().warn(getLogObject(obj));
	}

	private static Object getLogObject(Object e) {

		if (org.xml.sax.SAXParseException.class.isInstance(e)) {
			org.xml.sax.SAXParseException me = (org.xml.sax.SAXParseException) e;
			String str = me.getSystemId() + ":" + me.getLineNumber() + ":"
					+ me.getColumnNumber() + ":" + me.getMessage();
			return str;
		} else if (java.lang.NullPointerException.class.isInstance(e)) {
			java.lang.NullPointerException me = (java.lang.NullPointerException) e;
			me.printStackTrace();
			return me;
		} else if (Project.class.isInstance(e)) {
			Project project = (Project) e;
			StringBuffer buffer = new StringBuffer();
			buffer.append(project.toString() + "\r\n");
			buffer.append(project.getBrowser().toString() + "\r\n");
			int totalpages = 0;
			int totalelements = 0;
			int totalmodule = 0;
			int totaltestsuite = 0;
			int totaltestcase = 0;

			for (int i = 0; i < project.getInputs().size(); i++) {
				int moduleelements = 0;
				Object obj = ((Input) project.getInputs().get(i)).getObject();
				if (Module.class.isInstance(obj)) {
					totalmodule++;
					Module module = (Module) obj;
					buffer.append(module.toString() + "\r\n");
					for (int j = 0; j < module.getPages().size(); j++) {
						Page page = (Page) module.getPages().get(j);
						totalpages++;
						buffer.append(module.getName() + " " + page.toString()
								+ "element count:" + page.getElements().size()
								+ "\r\n");

						for (int k = 0; k < page.getElements().size(); k++) {
							Element element = (Element) page.getElements().get(
									k);
							totalelements++;
							moduleelements++;
							buffer.append(page.getName() + " "
									+ element.toString() + "\r\n");
						}
					}
					buffer.append(module.getName() + " page count:"
							+ module.getPages().size() + " element count:"
							+ moduleelements + "\r\n");
				} else if (TestSuite.class.isInstance(obj)) {
					totaltestsuite++;
					TestSuite testsuite = (TestSuite) obj;
					buffer.append(testsuite.toString() + "\r\n");
					int testcasecount = 0;
					for (int j = 0; j < testsuite.getTestcases().size(); j++) {
						TestCase testcase = (TestCase) testsuite.getTestcases()
								.get(j);
						totaltestcase++;
						testcasecount++;
						buffer.append(testsuite.getName() + " "
								+ testcase.toString() + "\r\n");

					}
					buffer.append(testsuite.getName() + " testcase count:"
							+ testcasecount + "\r\n");
				}

			}
			buffer.append("total module count:" + totalmodule
					+ " total page count:" + totalpages
					+ " total element count:" + totalelements + "\r\n");
			buffer.append("total testsuite count:" + totaltestsuite
					+ " total testcase count:" + totaltestcase + "\r\n");

			return buffer.toString();

		}

		return e;
	}

	public static java.awt.Robot getRobot() {
		if (robot != null)
			return robot;
		GraphicsEnvironment ge = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		try {
			robot = new java.awt.Robot(ge.getDefaultScreenDevice());
		} catch (Exception me) {
			me.printStackTrace();
		}
		return robot;
	}

	public static Logger getLogger() {
		if (logger != null)
			return logger;
		PropertyConfigurator.configure("log4j.properties");
		logger = Logger.getLogger("log4j");
		return logger;
	}

	public static Page getCurrentPage() {
		if (stackPages.empty()) {
			return null;
		}
		Page page = (Page) stackPages.peek();
		return page;
	}

	public static LinkedList getLinkActions() {
		return linkActions;
	}

	public static void setCurrentPage(Page page) {
		stackPages.push(page);
	}

	public static void removeCurrentPage() {
		stackPages.pop();
	}

	public static void clearPage() {
		stackPages.clear();
	}

	public static void putDynamicPara(String key, String value) {
		if (dynamicParaMap == null) {
			dynamicParaMap = new HashMap();
		}
		if (dynamicParaMap.containsKey(key)) {
			dynamicParaMap.remove(key);
		}
		//check value is dynamicPara?
		if(value.startsWith("{") && value.endsWith("}") && dynamicParaMap.containsKey(value.substring(1,value.length()-1))){
			value = (String)dynamicParaMap.get(value.substring(1,value.length()-1));
		}
		dynamicParaMap.put(key, value);
	}

	public static void putDynamicProp(String key, String value) {
		if (dynamicPropMap == null) {
			dynamicPropMap = new HashMap();
		}
		if (dynamicPropMap.containsKey(key)) {
			dynamicPropMap.remove(key);
		}
		dynamicPropMap.put(key, value);
	}

	public static void putDynamicVar(String key, String value) {
		if (dynamicVarMap == null) {
			dynamicVarMap = new HashMap();
		}
		if (dynamicVarMap.containsKey(key)) {
			dynamicVarMap.remove(key);
		}
		dynamicVarMap.put(key, value);
	}

	/**
	 * Compare to the BufferedImage acccording to the rgb.
	 * @param bi1 BufferedImage
	 * @param bi2 BufferedImage
	 * @return true if the two bufferedIamge is equal.
	 */
	public static boolean compareBufferedImage(BufferedImage bi1,
			BufferedImage bi2) {
		try {
			int h1 = bi1.getHeight();
			int w1 = bi1.getWidth();
			int h2 = bi2.getHeight();
			int w2 = bi2.getWidth();
			if (h1 != h2 || w1 != w2) {
				return false;
			}
			for (int i = 0; i < w1; i++) {
				for (int j = 0; j < h1; j++) {
					int biRGB1 = bi1.getRGB(i, j);
					int biRGB2 = bi2.getRGB(i, j);
					if (biRGB1 != biRGB2) {
						return false;
					}
				}
			}
		} catch (Exception e) {
			Util.error(e);
			return false;
		}
		return true;
	}

	/**
	 * Compare to the two image from the given path.
	 * @param imageFilePath1 String that defines the path of the image1
	 * @param imageFilePath2 String that defines the path of the image2
	 * @return true if the two image is equal.
	 */
	public static boolean compareImage(String imageFilePath1,
			String imageFilePath2) {

		BufferedImage bi1;
		BufferedImage bi2;
		try {
			bi1 = ImageIO.read(new File(imageFilePath1));
			bi2 = ImageIO.read(new File(imageFilePath2));
		} catch (IOException e) {
			Util.error(e);
			return false;
		}
		return compareBufferedImage(bi1, bi2);
	}

	/**
	 * 
	 * @return the unique id for the action in the current project.
	 */
	public static String generateActionId() {
		return generateId(Util.ACTION_ID);
	}

	/**
	 * 
	 * @return the unique id for the project in the current project.
	 */
	public static String generateProjectId() {
		return generateId(Util.PROJECT_ID);
	}

	/**
	 * 
	 * @return the unique id for the testSuite in the current project.
	 */
	public static String generateTestSuitId() {
		return generateId(Util.TESTSUIT_ID);
	}

	/**
	 * 
	 * @return the unique id for the testCase in the current project.
	 */
	public static String generateTestCaseId() {
		return generateId(Util.TESTCASE_ID);
	}

	/**
	 * 
	 * @return the unique id for the command in the current project.
	 */
	public static String generateCommandId() {
		return generateId(Util.COMMAND_ID);
	}

	/**
	 * 
	 * @return the unique id for the reportBean in the current project.
	 */
	public static String generateReportBeanId() {
		return generateId(Util.REPORTBEAN_ID);
	}

	/**
	 * Generate the uniqu id from the given type.
	 * @param type
	 * @return id - String
	 */
	public static String generateId(String type) {
		StringBuffer sb = new StringBuffer();
		if (ACTION_ID.equals(type)) {
			sb.append("action_");
			sb.append(System.currentTimeMillis() + actionId++);
		} else if (PROJECT_ID.equals(type)) {
			sb.append("project_");
			sb.append(System.currentTimeMillis() + projectId++);
		} else if (Util.TESTCASE_ID.equals(type)) {
			sb.append("testCase_");
			sb.append(System.currentTimeMillis() + testCaseId++);
		} else if (Util.TESTSUIT_ID.equals(type)) {
			sb.append("testSuit_");
			sb.append(System.currentTimeMillis() + testSuitId++);
		} else if (Util.COMMAND_ID.equals(type)) {
			sb.append("command_");
			sb.append(System.currentTimeMillis() + commandId++);
		} else if (Util.REPORTBEAN_ID.equals(type)) {
			sb.append("report_");
			sb.append(reportBeanId++);
		}
		return sb.toString();
	}

	/**
	 * Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
	 * @param obj the object the underlying method is invoked from
	 * @param methodName the name of the method
	 * @param args the arguments used for the method call 
	 * @return the result of dispatching the method represented by this object on obj with parameters args 
	 */
	public static Object invokeClassOfMethod(Object obj, String methodName,
			Object[] args) {
		Class c = obj.getClass();
		Method[] methods = c.getMethods();
		if (methods == null) {
			return null;
		}
		for (int i = 0; i < methods.length; i++) {
			if (methodName.equals(methods[i].getName())) {
				try {
					return methods[i].invoke(obj, args);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * Gets a string for the given key from the resource bundle or one of its parents.
	 * @param key
	 * @return value - String
	 */
	public static String getString(String key) {
		String result = key;
		String resourceKey = null;
		String keyStr = null;

		while (true) {
			if (result.startsWith("@")) {
				int index = result.indexOf(".");
				resourceKey = result.substring(1, index);
				keyStr = result.substring(index + 1);
				//use current proejct
				Project project = ProjectContext.getInstance(null).getProject();
				if (project == null) {
					return null;
				}
				List<Resource> resources = project.getResources();
				for (Resource resource : resources) {
					if (resourceKey.equalsIgnoreCase(resource.getKey())) {
						if (ResourceBundle.class.isInstance(resource.getObj())) {
							ResourceBundle rb = (ResourceBundle) resource
									.getObj();
							String temp = rb.getString(keyStr);
							result = getDynamicString(temp);
							break;
						}
					}
				}
			} else {
				result = getDynamicString(result);
			}
			
			if(!result.startsWith("@")){
				break;
			}			
			
		}

		return result;

	}
    public synchronized static HashMap getDynamicPara(){
    	return (HashMap)dynamicParaMap.clone();
    }
	public static String getDynamicString(String str) {
		String result = str;

		if (result.indexOf("{") < 0 || result.indexOf("}") < 0
				|| (dynamicParaMap == null || dynamicParaMap.size() == 0)) {
			return result;
		}
		while (true) {
			boolean hasPara = false;
			HashMap temp = (HashMap)getDynamicPara();
			Iterator iterator = temp.keySet().iterator();
			while (iterator.hasNext()) {
				String key = (String) iterator.next();
				if (result.indexOf("{" + key + "}") > 0) {
					hasPara = true;
					break;
				}
			}
			if (!hasPara) {
				return result;
			}

			

			HashMap temp1 = (HashMap)getDynamicPara();
			Iterator iterator1 = temp1.keySet().iterator();
			while (iterator1.hasNext()) {
				String key = (String) iterator1.next();
				String value = (String) temp1.get(key);
				value = Util.demaskTags(value);
				result = result.replace("{" + key + "}", value);
			}
		}

	}

	public static Object loadObjectFromFile(String file, Class c) {
		try {
			File input = new File(file);
			JAXBContext context = JAXBContext.newInstance(c);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			return unmarshaller.unmarshal(input);
		} catch (Exception e) {
			Util.error("load object from file failure " + file + e);
		}
		return null;
	}

	public static String loadFile(String path) {
		StringBuffer buffer = new StringBuffer();
		try {
			File file = new File(path);
			java.io.FileInputStream in = new java.io.FileInputStream(file);
			int count = 0;
			byte[] bytes = new byte[1024];
			while ((count = in.read(bytes)) > 0) {
				buffer.append(new String(bytes, 0, count));
			}
			in.close();
		} catch (Exception e) {
			Util.error("load content from file failure " + path + e);
		}
		return buffer.toString();
	}

	public static HashMap getDynamicParaMap() {
		return dynamicParaMap;
	}

	public static void setDynamicParaMap(HashMap dynamicMap) {
		Util.dynamicParaMap = dynamicMap;
	}

	public static String demaskTags(String s) {
		String str = replace(s, "&lt;br&gt;", "\n");
		str = replace(str, "&lt;", "<");
		str = replace(str, "&gt;", ">");
		str = replace(str, "&quot;", "\"");
		str = replace(str, "&apos;", "'");
		str = replace(str, "&amp;", "&");
		return str;
	}

	public static String markupTags(String s) {
		String str = replace(s, "&", "&amp;");
		str = replace(str, "<", "&lt;");
		str = replace(str, ">", "&gt;");
		str = replace(str, "\"", "&quot;");
		str = replace(str, "\r\n", "&lt;br&gt;");
		str = replace(str, "\n", "&lt;br&gt;");
		str = replace(str, "'", "&apos;");
		return str;
	}

	public static String replace(String s1, String s2, String s3) {
		if (s1 == null)
			return null;

		int inc = s3.length() - s2.length();
		int offset = 0;
		int fromIndex = 0;
		int findIndex = s1.indexOf(s2, fromIndex);
		StringBuffer buf = new StringBuffer(s1);
		while (findIndex != -1) {
			buf.delete(findIndex + offset, findIndex + offset + s2.length());
			buf.insert(findIndex + offset, s3);
			offset += inc;
			fromIndex = findIndex + s2.length();
			findIndex = s1.indexOf(s2, fromIndex);
		}

		String result = buf.toString();
		return result;
	}

	public static String getNodeValue(String xmlStr, String nodeName) {
		String value = null;
		int pos1 = xmlStr.indexOf("<" + nodeName + ">");
		int pos2 = xmlStr.indexOf("</" + nodeName + ">");
		if (pos1 > 0 && pos2 > 0 && pos2 > pos1) {
			value = xmlStr.substring(pos1 + nodeName.length() + 2, pos2);
		}
		return value;
	}

	public static HashMap getDynamicPropMap() {
		return dynamicPropMap;
	}

	public static void setDynamicPropMap(HashMap dynamicPropMap) {
		Util.dynamicPropMap = dynamicPropMap;
	}

	public static HashMap getDynamicVarMap() {
		return dynamicVarMap;
	}

	public static void setDynamicVarMap(HashMap dynamicVarMap) {
		Util.dynamicVarMap = dynamicVarMap;
	}


}
